package com.easylinkin.emp.utils;

import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import javax.annotation.PostConstruct;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import redis.clients.jedis.Jedis;

/**
 * @author ouyang
 * 基于redis的同步器，可以实现资源的申请与释放
 */
@Component
public class RedisSync {

    // 锁名称
    public static final String LOCK_PREFIX = "redis_lock:";

    @Autowired
    private StringRedisTemplate redisTemplate;

    static final Map<String, String> lockKeys = new ConcurrentHashMap<>();

    static ScheduledExecutorService ex = Executors.newScheduledThreadPool(1);

    /**
     * 更新锁的生存时间，防止生存时间到了任务还没做完的情况
     */
    @PostConstruct
    public void init() {
        ex.scheduleAtFixedRate(() -> {
            if (CollectionUtil.isNotEmptyMap(lockKeys)) {
                Iterator<Map.Entry<String, String>> iterator = lockKeys.entrySet().iterator();
                while (iterator.hasNext()) {
                    Map.Entry<String, String> next = iterator.next();
                    String key = next.getKey();
                    redisTemplate.expire(key, 10, TimeUnit.MINUTES);
                }
            }
        }, 1, 5, TimeUnit.MINUTES);
    }

    /**
     * 申请成功执行callback，执行完成自动释放
     */
    public void tryAcquireRelease(String key, Runnable callback) {
        if (this.tryAcquire(key)) {
            try {
                callback.run();
            } finally {
                this.release(key);
            }
        }
    }

    /**
     * 释放
     */
    public void release(String key) {
        String lock = LOCK_PREFIX + key;
        lockKeys.remove(lock);
        redisTemplate.delete(lock);
    }

    /**
     * 申请
     */
    public boolean tryAcquire(String key) {
        String lockKey = LOCK_PREFIX + key;
        String value = String.valueOf(System.currentTimeMillis());
        String statusCode = redisTemplate.execute(new RedisCallback<String>() {
            @Override
            public String doInRedis(RedisConnection connection) {
                Jedis conn = (Jedis) connection.getNativeConnection();
                return conn.set(lockKey, value, "NX", "EX", TimeUnit.MINUTES.toSeconds(10));
            }
        }, true);

        if ("OK".equals(statusCode)) {
            lockKeys.put(lockKey, lockKey);
            return true;
        }

        return false;
    }

}
